/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { AliasType, ArrayType, ClassType, FunctionType, GenericType, TupleType, Type, UnclearReferenceType, UnionType } from "arkanalyzer";

/**
 * 检查类型是否为指定类型
 * 
 * @param appointType 指定类型
 * @param type 被检查类型
 * @returns 
 */
export function isAppointType(appointType: Type, type: Type): boolean {

    if (appointType.getTypeString() === type.getTypeString()) {
      return true;
    }

    if (type instanceof ArrayType) {
      return isAppointType(appointType, type.getBaseType());
    }

    if (type instanceof UnclearReferenceType) {
      let isAny = false;
      for (const t of type.getGenericTypes() || []) {
        isAny = isAppointType(appointType, t);
        if (isAny) return isAny;
      }
      return isAny;
    }

    if (type instanceof UnionType) {
      let isAny = false;
      for (const t of type.getTypes() || []) {
        isAny = isAppointType(appointType, t);
        if (isAny) return isAny;
      }
      return isAny;
    }

    if (type instanceof FunctionType) {
      let isAny = false;
      for (const t of type.getRealGenericTypes() || []) {
        isAny = isAppointType(appointType, t);
        if (isAny) {
            break;
        }
      }
      return isAny;
    }

    if (type instanceof ClassType) {
      let isAny = false;
      for (const t of type.getRealGenericTypes() || []) {
        isAny = isAppointType(appointType, t);
        if (isAny) {
            break;
        }
      }
      return isAny;
    }

    if (type instanceof TupleType) {
      let isAny = false;
      for (const t of type.getTypes() || []) {
        isAny = isAppointType(appointType, t);
        if (isAny) {
            break;
        }
      }
      return isAny;
    }

    if (type instanceof AliasType) {
      return isAppointType(appointType, type.getOriginalType());
    }

    if (type instanceof GenericType) {
      let isDefTypeAny = false;
      let isConstraintTypeAny = false;
      let defType = type.getDefaultType();
      let constraintType = type.getConstraint();
      if (defType) {
        isDefTypeAny = isAppointType(appointType, defType);
      }
      if (constraintType) {
        isConstraintTypeAny = isAppointType(appointType, constraintType);
      }
      return isDefTypeAny || isConstraintTypeAny;
    }
    return false;
  }

  /**
   * 递归替换类型中的指定类型
   * 
   * @param appointType 指定类型
   * @param type 需要被替换的类型
   * @param newType 新的类型
   * @returns 
   */
  export function fixAppointType(appointType: Type, type: Type, newType: Type): Type {
    if (appointType.getTypeString() === type.getTypeString()) {
        return newType;
      }

    if (type instanceof ArrayType) {
      type.setBaseType(fixAppointType(appointType, type.getBaseType(), newType));
      return type;
    }

    if (type instanceof UnclearReferenceType) {
      const genericTypes = type.getGenericTypes() || [];
      for (let i = 0; i < genericTypes.length; i++) {
          genericTypes[i] = fixAppointType(appointType, genericTypes[i], newType);
      }
      return new UnclearReferenceType(type.getName(), genericTypes);
    }

    if (type instanceof UnionType) {
      const unionTypes = type.getTypes() || [];
      for (let i = 0; i < unionTypes.length; i++) {
          unionTypes[i] = fixAppointType(appointType, unionTypes[i], newType);
      }
      return new UnionType(unionTypes);
    }

    if (type instanceof FunctionType) {
      const realGenericTypes = type.getRealGenericTypes() || [];
      for (let i = 0; i < realGenericTypes.length; i++) {
          realGenericTypes[i] = fixAppointType(appointType, realGenericTypes[i], newType);
      }
      return new FunctionType(type.getMethodSignature(), realGenericTypes);
    }

    if (type instanceof ClassType) {
      const realGenericTypes = type.getRealGenericTypes() || [];
      for (let i = 0; i < realGenericTypes.length; i++) {
          realGenericTypes[i] = fixAppointType(appointType, realGenericTypes[i], newType);
      }
      type.setRealGenericTypes(realGenericTypes);
      return type;
    }

    if (type instanceof TupleType) {
      const tupleTypes = type.getTypes() || [];
      for (let i = 0; i < tupleTypes.length; i++) {
          tupleTypes[i] = fixAppointType(appointType, tupleTypes[i], newType);
      }
      return new TupleType(tupleTypes);
    }

    if (type instanceof AliasType) {
      type.setOriginalType(fixAppointType(appointType, type.getOriginalType(), newType));
      return type;
    }

    if (type instanceof GenericType) {
      let defType = type.getDefaultType();
      let constraintType = type.getConstraint();
      if (defType) {
        defType = fixAppointType(appointType, defType, newType);
      }
      if (constraintType) {
        constraintType = fixAppointType(appointType, constraintType, newType);
      }
      if(defType) type.setDefaultType(defType);
      if(constraintType) type.setConstraint(constraintType);
      return type;
    }
    return type;
  }